﻿#include "Timer.h"
#ifndef WIN32
#include <sys/timerfd.h>
#endif // !WIN32
#include <time.h>
#include <chrono>
#include "Event.h"
#include "EventScheduler.h"
#include "Poller.h"
#include "../Base/Log.h"



//struct timespec {
//    time_t tv_sec; //Seconds
//    long   tv_nsec;// Nanoseconds
//};
//struct itimerspec {
//    struct timespec it_interval;  //Interval for periodic timer （定时间隔周期）
//    struct timespec it_value;     //Initial expiration (第一次超时时间)
//};
//    it_interval不为0 表示是周期性定时器
//    it_value和it_interval都为0 表示停止定时器


static bool timerFdSetTime(int fd, Timer::Timestamp when, Timer::TimeInterval period) {

#ifndef WIN32
    struct itimerspec newVal;

    //触发时间
    newVal.it_value.tv_sec = when / 1000; //ms->s
    newVal.it_value.tv_nsec = when % 1000 * 1000 * 1000; //ms->ns
    //周期
    newVal.it_interval.tv_sec = period / 1000;
    newVal.it_interval.tv_nsec = period % 1000 * 1000 * 1000;

    //配置定时器参数，周期和初值
    int oldValue = timerfd_settime(fd, TFD_TIMER_ABSTIME, &newVal, NULL);
    if (oldValue < 0) {
        return false;
    }
    return true;
#endif // !WIN32

    return true;
}


Timer::Timer(TimerEvent* event, Timestamp timestamp, TimeInterval timeInterval, TimerId timerId) :
        mTimerEvent(event),
        mTimestamp(timestamp),
        mTimeInterval(timeInterval),
        mTimerId(timerId){
    if (timeInterval > 0){
        mRepeat = true;// 循环定时器，需要设置起始时间和周期
    }else{
        mRepeat = false;//一次性定时器，仅需设置定时时间
    }
}

Timer::~ Timer()
{

}
// 获取系统从启动到目前的毫秒数
Timer::Timestamp Timer::getCurTime(){
#ifndef WIN32
    // Linux系统
    struct timespec now;// tv_sec (s) tv_nsec (ns-纳秒)
    clock_gettime(CLOCK_MONOTONIC, &now);
    return (now.tv_sec*1000 + now.tv_nsec/1000000);
#else
    long long now = std::chrono::steady_clock::now().time_since_epoch().count();
    return now / 1000000;
#endif // !WIN32

}
Timer::Timestamp Timer::getCurTimestamp() {
    return std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::system_clock::now().time_since_epoch()).
            count();

}
bool Timer::handleEvent()
{
    if (!mTimerEvent) {
        return false;
    }
    return  mTimerEvent->handleEvent();

}

TimerManager* TimerManager::createNew(EventScheduler* scheduler){

    if(!scheduler)
        return NULL;
    return new TimerManager(scheduler);
}

TimerManager::TimerManager(EventScheduler* scheduler) :
        mPoller(scheduler->poller()),
        mLastTimerId(0){

#ifndef WIN32
    //如果有100路流，可能就需要100个定时器的配置，但只有一个timerfd，大家轮流使用
    //创建定时器，获取定时器文件描述符(linux系统定时器，timerfd),一个timerfd只能配置一个定时器，同时支持一个定时任务
    //linux计时器timerfd超时时，查找std::multimap<Timer::Timestamp, Timer>,获取合适的计时器timer,调用timer事件设置的回调函数
    mTimerFd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);

    if (mTimerFd < 0) {
        LOGE("create TimerFd error");
        return;
    }else{
        LOGI("fd=%d",mTimerFd);
    }
    //创建定时器事件，事件绑定linux定时器描述符，设置回调函数。描述符和回调函数就像qt中的信号和槽
    mTimerIOEvent = IOEvent::createNew(mTimerFd, this);
    mTimerIOEvent->setReadCallback(readCallback);
    mTimerIOEvent->enableReadHandling();
    //配置定时器，将定时器实际配置到timerfd上
    modifyTimeout();
    //使用网络模型，将文件描述符和回调函数联系起来
    mPoller->addIOEvent(mTimerIOEvent);
#else
    scheduler->setTimerManagerReadCallback(readCallback, this);

#endif // !WIN32


}

TimerManager::~TimerManager()
{
#ifndef WIN32
    mPoller->removeIOEvent(mTimerIOEvent);
    delete mTimerIOEvent;
#endif // !WIN32

}

//添加一个定时器，先创建一个定时器，然后将它实际配置到linux timerfd上
Timer::TimerId TimerManager::addTimer(TimerEvent* event, Timer::Timestamp timestamp,
                                      Timer::TimeInterval timeInterval)
{
    ++mLastTimerId;
    //timeInterval=40或者43,timestamp=系统从启动到目前的毫秒数+40或者43
    //循环定时器
    Timer timer(event, timestamp, timeInterval,mLastTimerId);


    mTimers.insert(std::make_pair(mLastTimerId, timer));
    //mEvents.insert(std::make_pair(TimerIndex(timestamp, mLastTimerId), timer));
    mEvents.insert(std::make_pair(timestamp, timer));
    //配置timerfd定时器，
    modifyTimeout();

    return mLastTimerId;
}

bool TimerManager::removeTimer(Timer::TimerId timerId)
{
    std::map<Timer::TimerId, Timer>::iterator it = mTimers.find(timerId);
    if(it != mTimers.end())
    {
        mTimers.erase(timerId);
        // TODO 还需要删除mEvents的事件
    }

    modifyTimeout();

    return true;
}

void TimerManager::modifyTimeout()
{
#ifndef WIN32
    //视频一秒25帧，间隔40ms,音频一秒43帧，间隔23ms
    //std::multimap默认按照键值的升序进行排序
    //设置timerfd的时候，是取mEvents的头元素，所以，触发的时候，也要取mEvents的头元素
    std::multimap<Timer::Timestamp, Timer>::iterator it = mEvents.begin();
    if (it != mEvents.end()) {// 存在至少一个定时器
        Timer timer = it->second;
        //配置定时器的触发时间和周期
        timerFdSetTime(mTimerFd, timer.mTimestamp, timer.mTimeInterval);
    }
    else {
        timerFdSetTime(mTimerFd, 0, 0);
    }
#endif // WIN32

}

void TimerManager::readCallback(void *arg) {
    TimerManager* timerManager = (TimerManager*)arg;
    timerManager->handleRead();
}

//100路流，可能就有100/200/400个定时器的配置,它们轮流占用timerfd
void TimerManager::handleRead() {

    //LOGI("mTimers.size()=%d,mEvents.size()=%d",mTimers.size(),mEvents.size());
    Timer::Timestamp timestamp = Timer::getCurTime();//获取当前系统启动以来的毫秒数
    if (!mTimers.empty() && !mEvents.empty()) {

        //multimap可以存放重复元素，键相同或者键值都相同的元素
        //设置timerfd的时候，是取mEvents的头元素，所以，触发的时候，也要取mEvents的头元素
        std::multimap<Timer::Timestamp, Timer>::iterator it = mEvents.begin();//获取最小的键值对，
        Timer timer = it->second;
        int expire = timer.mTimestamp - timestamp;//定时器时间戳减当前时间戳

        //LOGI("timestamp=%d,mTimestamp=%d,expire=%d,timeInterval=%d", timestamp, timer.mTimestamp, expire, timer.mTimeInterval);

        //如果当前时间戳大于定时器时间戳或者相等
        if (timestamp > timer.mTimestamp || expire == 0) {//定时时间到

            //超时回调,流未关闭时，timerEventIsStop为默认false
            bool timerEventIsStop = timer.handleEvent();
            //删除定时器
            mEvents.erase(it);//从map1中删除已触发的定时器,实际上删除了map1中时间戳最小的定时器
            //循环定时器
            if (timer.mRepeat) {
                //如果执行成功，timerEventIsStop为false
                if (timerEventIsStop) {
                    mTimers.erase(timer.mTimerId);//从map2中删除已触发的定时器
                }else {
                    //如果执行完成，就更新这个定时器和map1,这里把map当队列用了
                    timer.mTimestamp = timestamp + timer.mTimeInterval;//更新时间戳，这里手动更新时间戳，而timerfd计时器是自动触发
                    mEvents.insert(std::make_pair(timer.mTimestamp, timer));//重新插入map1尾部
                }
            }
            else {//一次性定时器，
                mTimers.erase(timer.mTimerId);
            }

        }
    }
    //下一个定时器占用timerfd
    modifyTimeout();
}